package com.dai.dao;

import org.apache.tomcat.dbcp.dbcp.BasicDataSource;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

public class BaseDAO<T>{

    private static final BasicDataSource dataSource = new BasicDataSource();
    static {

        /**
         * 一些常用的属性:
         *         defaultAutoCommit：设置从数据源中返回的连接是否采用自动提交机制，默认值为 true；
         *         defaultReadOnly：设置数据源是否仅能执行只读操作， 默认值为 false；
         *         maxActive：最大连接数据库连接数，设置为0时，表示没有限制；
         *         maxIdle：最大等待连接中的数量，设置为0时，表示没有限制；
         *         maxWait：最大等待秒数，单位为毫秒， 超过时间会报出错误信息；
         *         validationQuery：用于验证连接是否成功的查询SQL语句，SQL语句必须至少要返回一行数据，
         *                           如你可以简单地设置为：“select count(*) from user”；
         *         removeAbandoned：是否自我中断，默认是 false ；
         *         removeAbandonedTimeout：几秒后数据连接会自动断开，在removeAbandoned为true，提供该值；
         *         logAbandoned：是否记录中断事件， 默认为 false；
         */
        dataSource.setDriverClassName("org.sqlite.JDBC");
        dataSource.setUrl("jdbc:sqlite:mytestdb.db");
        dataSource.setMaxActive(100); // 最大连接数据库连接数，设置为0时，表示没有限制；
        dataSource.setMaxIdle(10); // 最大等待连接中的数量，设置为0时，表示没有限制；
        dataSource.setMaxWait(10000); // 最大等待秒数，单位为毫秒， 超过时间会报出错误信息；
        dataSource.setValidationQuery("select 1"); // 用于验证连接是否成功的查询SQL语句，SQL语句必须至少要返回一行数据，如你可以简单地设置为：“select count(*) from user”；

        dataSource.setRemoveAbandoned(false); // 是否自我中断，默认是 false ；
    }

    private final Class beanClz;
    public BaseDAO(){
        ParameterizedType paramType = (ParameterizedType) this.getClass().getGenericSuperclass();
        beanClz = (Class) paramType.getActualTypeArguments()[0];
    }

    /**
     * 关闭数据库连接
     * @param conn
     */
    public static void closeConnection(Connection conn) {
        if (null != conn) {
            try {
                conn.close();
                conn = null;
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 关闭数据库资源
     * @param stmt
     */
    public static void closeStatement(Statement stmt) {
        if (null != stmt) {
            try {
                stmt.close();
                stmt = null;
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }


    public Connection getConnection() throws SQLException {
        return  dataSource.getConnection();
    }

    public T get(String sql){
        Connection conn = null;
        Statement statement = null;
        T t = null;
        try {
            conn = getConnection();
            statement = conn.createStatement();
            ResultSet rs = statement.executeQuery(sql);
            while (rs.next()){
               t = getObjectFromResultSet(rs);
               return  t;
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }finally {
            closeStatement(statement);
            closeConnection(conn);
        }
        return  t;
    }

    public List<T> all(String sql){
        Connection conn = null;
        Statement statement = null;
        List<T> lst = new ArrayList<>();
        try{
            conn = getConnection();
            statement = conn.createStatement();
            ResultSet rs =  statement.executeQuery(sql);
            while (rs.next()){
                lst.add(getObjectFromResultSet(rs));
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } finally {
            closeStatement(statement);
            closeConnection(conn);
        }
        return  lst;
    }

    // DML 或者 DDL 语句
    public void run(String sql){
        Connection conn = null;
        Statement statement = null;
        try {
            conn = getConnection();
            statement = conn.createStatement();
            statement.executeUpdate(sql);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            closeStatement(statement);
            closeConnection(conn);
        }
    }

    private T getObjectFromResultSet(ResultSet rs) throws IllegalAccessException, InstantiationException, SQLException {
        T t = (T) beanClz.newInstance();
        Field[] fields = beanClz.getDeclaredFields();
        for(Field field : fields){
            int modifiers = field.getModifiers();
            if(!Modifier.isStatic(modifiers)){
                field.setAccessible(true);
                Class clz = field.getType();
                if(clz == int.class || clz == Integer.class){
                    field.set(t,rs.getInt(field.getName()));
                }else if(clz == float.class || clz == Float.class){
                    field.set(t,rs.getFloat(field.getName()));
                }else if(clz == double.class || clz == Double.class){
                    field.set(t,rs.getDouble(field.getName()));
                }else if(clz == String.class){
                    field.set(t,rs.getString(field.getName()));
                }
            }
        }
        return  t;
    }

    public int getIntId(String sql){
        Connection conn = null;
        Statement statement = null;
        T t = null;
        try {
            conn = getConnection();
            statement = conn.createStatement();
            ResultSet rs = statement.executeQuery(sql);
            while (rs.next()){
               return rs.getInt(1);
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            closeStatement(statement);
            closeConnection(conn);
        }
        return  -1;
    }
}
